Skip to content

fix: move the improvement log out of the tree behind an accessor - #59

Merged
stranske merged 1 commit into
mainfrom
claude/backlog-accessor
Aug 23, 2026
Merged

fix: move the improvement log out of the tree behind an accessor#59
stranske merged 1 commit into
mainfrom
claude/backlog-accessor

Conversation

@stranske

@stranske stranske commented Aug 23, 2026

Copy link
Copy Markdown
Owner

The defect

CLAUDE.md depended on IMPROVEMENT_BACKLOG.md in two places and both were unfollowable by exactly the workers they bind:

  • §0 step 3, part of the MANDATORY dedup-before-develop check — "check the improvement log; items carry status notes; many 'ideas' are already DONE"
  • §5"append a status note to the relevant IMPROVEMENT_BACKLOG.md item"

The file was gitignored at .gitignore:63. A gitignored file does not exist in a git worktree, and agents work in worktrees — verified absent from every worktree on this machine. Three agents in one day were structurally unable to do step 3; two said so and fell back to ledger notes and committed docstrings. 481 KB / 207 sections of accumulated "already DONE" knowledge — the project's stated #1-failure-mode countermeasure — was invisible to every worker.

The fix

Moved out of the tree to $ORCH_LOCAL_RUNTIME, per CLAUDE.md §1's own rule for runtime state and durable evidence.

Why ORCH_LOCAL_RUNTIME and not ORCH_STATE_DIR. §1 already partitions them: ORCH_STATE_DIR holds the audit cache, firing monitor and redirect-sweep state — derived, self-regenerating — while ORCH_LOCAL_RUNTIME holds the capability ledger and the Brain: durable, irreplaceable instance evidence. The improvement log is the second kind; it cannot be regenerated from anything, and its §0 role is the prose twin of the ledger's notes field (both answer "is this already DONE?"). Useful consequence: ci.yml already points ORCH_LOCAL_RUNTIME at an empty temp dir, so the named-absence path is exercised on every runner by construction.

The move was copy → verify → remove, in that order — 482,865 bytes, sha256 f7eea973…d16d identical at source and destination before anything was deleted. There is no git undo for unversioned history.

The accessor is improvement_log.py (backlog.py is taken by the unrelated fleet work-discovery lane and is untouched):

command serves
search <term> §0 step 3, in one command. Each hit printed under the item heading that owns it, plus lines/sections read, so "no matching items" describes a file that was actually read
append <ref> <note> §5, in one command. Dated note placed inside the matched item, atomically, one rolling backup. REFUSES on an ambiguous or unknown ref rather than guessing — a note filed against the wrong item corrupts the record it exists to improve
path where it resolved to, and whether it is here

A named absence, never a silent one. On a fresh clone, a runner, or a second instance there is no log. Every command then names what is missing, where it would be, and both env vars that control it, and exits 2. An honest empty is exit 1 and says so in words. A reason-less empty result is indistinguishable from "no matches" — this repo's founding defect.

The committed pointer is what actually fixes the invisibility. IMPROVEMENT_BACKLOG.md is now tracked and holds a 1.8 KB pointer, so all thirteen citations of that filename still resolve and ls in a worktree still shows it. Removing it from .gitignore is deliberate: left ignored, the 481 KB file could reappear at that path silently; tracked, it announces itself in git status. The real log now lives outside the repository entirely, so git add on it fails with outside repository — stronger than an ignore rule. test_improvement_log.py fails if the pointer grows into a log.

capability_admission.SKIP_NAMES drops its IMPROVEMENT_BACKLOG.md entry — the allowlist reason ("the backlog narrates history and cites closed records") no longer describes anything in the tree, so the gate now scans one more file.

Dedup finding (CLAUDE.md §0)

Recorded before writing code and again in the module docstring, since plans are not durable. Grepped by concept, not name: git grep IMPROVEMENT_BACKLOG returns 13 references, every one prose or a comment — no reader, no writer, no accessor. improvement_backlog|improvement-backlog|backlog_notes over *.py returns nothing. The only machinery touching the filename was capability_admission.SKIP_NAMES, an exclusion. Genuinely absent → new.

No ledger row, deliberately. This is documentation access with no dispatch path, outcome or learning sink, so the admission gate does not bind on it — the same reasoning env_prereq.py records for itself. Registering one would also turn every sibling worktree's verify.py red for a module they cannot see (§1: ledger shared per machine, code per worktree).

Not an ARCHITECTURE.md change — repo tooling like verify.py and env_prereq.py, not a loop stage, rail, role, feedback surface or registry entry.

Verification

Measured on the merge result, after rebasing onto af6654d (PR #42, 143 files) — not on the branch base, per .verify-floor.json's own rule.

python3 verify.py: 391 passed, 0 failed, 0 skipped, 84/84 selftests, 43/43 can-fire, 5/5 gates. 391 = main's 387 + exactly these four tests.

Under the CI condition (both ORCH_STATE_DIR and ORCH_LOCAL_RUNTIME at empty dirs): 383 passed + 8 skipped = 391, exit 0. All four new tests run there — none reads a ledger, an agent CLI or ~/.codex — so no skip ceiling moved. Floor 387 → 391 for exactly those four tests.

Break→revert, nine cases

Each confirmed to fail while broken and pass after revert:

  1. absence returned as an empty result (--selftest)
  2. the same, asserted under pytest — what CI actually runs
  3. ambiguous append guessing hits[0] instead of refusing
  4. append writing at EOF instead of inside the matched item
  5. the two nothing-exit-codes (honest-empty vs absent) collapsed into one
  6. the pointer size gate — pointer padded toward log size
  7. the pointer no longer naming the accessor
  8. CLAUDE.md §0 step 3 reverted to a bare path
  9. CLAUDE.md §5 reverted to "edit the file"

Case 4 did NOT discriminate on the first attempt. The fixture appended to the last section, where "section end" and "end of file" are the same position, so the placement assertion could not fail. The fixture was rewritten to target a middle item and assert the note lands above the next heading; the hole is recorded in the selftest so it cannot come back.

🤖 Generated with Claude Code

@coderabbitai

coderabbitai Bot commented Aug 23, 2026

Copy link
Copy Markdown

Warning

Review limit reached

You’ve reached a temporary PR review limit under our Fair Usage Limits Policy.

Your current included review allowance is based on your included PR review attempts over the past 7 days.

Next review available in: 41 minutes

Limit details: You’ve used the included review currently available. Your 75 included PR review attempts over the past 7 days set your current allowance at 1 review per hour.

Your organization has reached its usage spending cap. Adjust your spending cap in the billing tab.

How can I continue?

Wait for the limit to reset, then comment @coderabbitai review or push new commits to the PR.

An organization admin can change what happens after included review limits in Billing.

How do review limits work?

CodeRabbit enforces per-developer PR review limits within each organization.

For paid Pro and Pro+ reviews, CodeRabbit uses a developer's included PR review attempts over the past 7 days to set the current hourly allowance. At typical activity levels, the full plan allowance applies. Higher sustained activity can lower the allowance until earlier attempts leave the 7-day window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 4a284674-6e54-4492-95a9-bd38fcd565ba

📥 Commits

Reviewing files that changed from the base of the PR and between af6654d and bef0811.

📒 Files selected for processing (9)
  • .gitignore
  • .verify-floor.json
  • ADDING_CAPABILITIES.md
  • CLAUDE.md
  • IMPROVEMENT_BACKLOG.md
  • README.md
  • capability_admission.py
  • improvement_log.py
  • test_improvement_log.py

Comment @coderabbitai help to get the list of available commands.

CLAUDE.md depended on IMPROVEMENT_BACKLOG.md in two places and both were
unfollowable by exactly the workers they bind. §0 step 3 ("check the improvement
log; many ideas are already DONE") is part of the MANDATORY dedup-before-develop
check, and §5 says append a status note there. The file was gitignored, and a
gitignored file does not exist in a git WORKTREE — it was verified absent from
every worktree on this machine. Agents work in worktrees. Three agents in one day
could not do step 3; two said so and fell back to ledger notes and docstrings.
481 KB of accumulated "already DONE" knowledge — the project's stated

WHICH STATE VARIABLE: ORCH_LOCAL_RUNTIME, not ORCH_STATE_DIR. CLAUDE.md §1
already partitions them — ORCH_STATE_DIR holds the audit cache, firing monitor
and redirect-sweep state, all derived and self-regenerating, while
ORCH_LOCAL_RUNTIME holds the capability LEDGER and the Brain: durable,
irreplaceable instance evidence. The improvement log is the second kind. It
cannot be regenerated from anything, and its §0 role is the prose twin of the
ledger's `notes` field (both answer "is this already DONE?"), so it belongs
beside the ledger rather than beside the caches. A useful consequence: ci.yml
already points ORCH_LOCAL_RUNTIME at an empty temp dir, so the named-absence
path is exercised on every runner by construction.

THE MOVE was copy -> verify -> remove, in that order: 482,865 bytes, sha256
f7eea973...d16d identical at source and destination before anything was deleted.
There is no git undo for unversioned history.

THE ACCESSOR is improvement_log.py (backlog.py is taken by the unrelated fleet
work-discovery lane and is not touched):
  search <term>        — §0 step 3 in one command. Prints each hit under the item
                         heading that owns it, plus the lines and sections read,
                         so "no matching items" describes a file actually read.
  append <ref> <note>  — §5 in one command. Places a dated note INSIDE the
                         matched item, atomically, keeping one rolling backup;
                         REFUSES on an ambiguous or unknown ref rather than
                         guessing, because a note filed against the wrong item
                         corrupts the record it exists to improve.
  path                 — where it resolved to, and whether it is here.

A NAMED ABSENCE, NEVER A SILENT ONE. On a fresh clone, a runner, or a second
instance there is no log; every command then names what is missing, where it
would be, and both env vars that control it, and exits 2. Honest-empty is exit 1
and says so in words. A reason-less empty result is indistinguishable from "no
matches", which is this repo's founding defect.

THE POINTER is what actually fixes the invisibility: nothing in a worktree even
hinted the log existed. IMPROVEMENT_BACKLOG.md is now TRACKED and holds a short
pointer, so all thirteen citations of that filename still resolve and `ls` still
shows it. It is removed from .gitignore deliberately: left ignored, the 481 KB
file could reappear at that path silently — tracked, it announces itself in
`git status`. The real log lives outside the repository entirely, so `git add`
on it fails with "outside repository", which is stronger than an ignore rule.
test_improvement_log.py fails if the pointer grows into a log.

capability_admission.SKIP_NAMES drops its IMPROVEMENT_BACKLOG.md entry: the
allowlist reason ("the backlog narrates history and cites closed records") no
longer describes anything in the tree, so the gate now scans one more file.

DEDUP FINDING (CLAUDE.md §0), recorded before writing code and again in the
module docstring since plans are not durable: grepped by concept, not name.
`git grep IMPROVEMENT_BACKLOG` returns 13 references, every one prose or a
comment — no reader, no writer, no accessor. `improvement_backlog|
improvement-backlog|backlog_notes` over *.py returns nothing. The only machinery
touching the filename was capability_admission.SKIP_NAMES, an exclusion.
Genuinely absent, so new. NO LEDGER ROW, deliberately: this is documentation
access with no dispatch path, outcome or learning sink, so the admission gate
does not bind on it — the same reasoning env_prereq.py records for itself — and
registering one would turn every sibling worktree's verify.py red for a module
they cannot see (§1, ledger shared per machine / code per worktree).

Not an ARCHITECTURE.md change: this is repo tooling like verify.py and
env_prereq.py, not a loop stage, rail, role, feedback surface or registry entry.

VERIFIED on the MERGE RESULT, after rebasing onto af6654d (PR #42, 143 files):
391 passed, 0 failed, 0 skipped, 84/84 selftests, 43/43 can-fire, 5/5 gates.
391 = main's 387 + exactly these four tests. Under the CI condition (both
ORCH_STATE_DIR and ORCH_LOCAL_RUNTIME at empty dirs): 383 passed + 8 skipped =
391, exit 0, and all four new tests RUN there — none reads a ledger, an agent
CLI or ~/.codex, so NO skip ceiling moved. Floor 387 -> 391 for exactly those
four tests, measured on the merge result rather than on the branch base.

Break->revert, nine cases, each confirmed to fail while broken and pass after
revert: absence-as-empty (under --selftest and under pytest separately);
ambiguous append guessing hits[0]; append writing at EOF instead of inside the
item; the two nothing-exit-codes collapsed into one; the pointer size gate; the
pointer no longer naming the accessor; CLAUDE.md §0 step 3 reverted to a bare
path; CLAUDE.md §5 reverted to "edit the file". The append-placement case did
NOT discriminate on the first attempt — the fixture appended to the LAST section,
where "section end" and "end of file" are the same position — so the fixture was
rewritten to target a middle item and assert the note lands above the next
heading. That hole is recorded in the selftest so it cannot come back.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@stranske
stranske force-pushed the claude/backlog-accessor branch from 46cc2f0 to bef0811 Compare August 23, 2026 13:33
@stranske-keepalive

Copy link
Copy Markdown

Workflow source needed

PR #59 needs either a linked GitHub issue or one valid non-issue Workflow Source before PR metadata automation can manage it safely.

Please do one of:

  • Add <!-- meta:issue:123 --> or a normal Closes #123 / Related to #123 line.
  • Check one Workflow Source option in the PR body.
  • Add a hidden marker such as <!-- workflow-source:local_request -->, <!-- workflow-source:manual_remote -->, <!-- workflow-source:review_followup -->, <!-- workflow-source:sync_campaign -->, or <!-- workflow-source:dependabot -->.
  • Add a workflow source label such as workflow:source-direct-pr, workflow:source-local-request, workflow:source-review-followup, workflow:source-sync, or workflow:no-automation.

Once a valid source is present, this warning will not be reposted.

@stranske-keepalive

Copy link
Copy Markdown

Automated Status Summary

Head SHA: 5c5b946
Latest Runs: ⏳ pending — Gate
Required: core tests (3.12): ⏳ pending, core tests (3.13): ⏳ pending, docker smoke: ⏳ pending, gate: ⏳ pending

Workflow / Job Result Logs
(no jobs reported) ⏳ pending

Coverage Overview

  • Coverage history entries: 0

Updated automatically; will refresh on subsequent CI/Docker completions.


Keepalive checklist

Scope

No scope information available

Tasks

  • No tasks defined

Acceptance criteria

  • No acceptance criteria defined

@stranske

Copy link
Copy Markdown
Owner Author

Runner dispatch state for codex on PR #59. Do not edit.

@stranske

Copy link
Copy Markdown
Owner Author

Runner dispatch state for autofix on PR #59. Do not edit.

@github-actions

github-actions Bot commented Aug 23, 2026

Copy link
Copy Markdown
Contributor

Workflow state fingerprint for Agents Gate Followups. Do not edit.

@stranske
stranske merged commit 5bd8de1 into main Aug 23, 2026
80 of 92 checks passed
agents-workflows-bot Bot pushed a commit that referenced this pull request Aug 23, 2026
stranske pushed a commit that referenced this pull request Aug 23, 2026
… silent

`.verify-floor.json` has been found BELOW reality four times -- 21 low at the worst, then 8,
then 1, then 2 -- and each was hand-raised after the fact by whoever happened to look. The
stale number was never the defect. `_floor_problems` fired only on `collected < floor`, so a
PR could add tests and never touch the file: silently green, with the floor left permissive
by exactly the number added. #34 and #37 each did precisely that, which is what PR #50 then
had to clean up by hand.

`collected` is now an equality. Too few tests still fails; too many fails as well, printing
the two integers to write and telling you to rebase first.

That also makes the concurrent case self-enforcing, which is the part discipline could not
fix. Once every test-adding branch must edit these same two lines, two concurrent branches
CONFLICT IN GIT -- the second cannot merge without rebasing onto the first and re-measuring
on the actual merge result. This change demonstrated that on itself TWICE inside an hour:
#42 landed underneath it (floor 368 -> 387, ceiling 24 -> 26) and then #59 did (387 -> 391),
and each conflict forced a rebase and a fresh measurement. Under the old one-directional
rule both would have merged green with a floor below reality. Git's own conflict detection
is what enforces "measure on the merge result, not on the branch", the rule the note in that
file had to restate three times with nothing behind it.

`passed` deliberately stays a MINIMUM on passed+skipped. Only collection is
machine-invariant -- a skipped test is still collected, measured the same day at 368 on both
CI and this machine with pass/skip splits of 344/24 against 368/0. Making that one strict
too would fail a machine for honestly naming a missing prerequisite.

Two further fixes in the same change:

* `--update-floor` no longer REPLACES the note. It appends. The note is the only record of
  which prerequisite justifies each ceiling, so overwriting it destroyed the rationale on
  every use -- the file had to carry a warning about its own tool.
* Drift does not block `--update-floor`. The first draft made it a latched gate: a floor
  behind reality became a problem, and the guard was `not problems`, so the one remedy the
  error message named was refused for the existence of the very condition it clears.
  `_blocks_floor_update` lets drift through while real failures still block, sharing
  `DRIFT_PREFIX` so message and predicate cannot diverge.

Selftests cover both directions, skip-invariance, the unset-floor case, the unblock
predicate and note preservation, each with a deliberate-break demonstration. Two of those
tests were themselves defective and the break demo is what caught it: the latched-gate
assert exercised `_blocks_floor_update` in isolation and stayed green when the CALL SITE was
reverted (built-but-not-wired), and the source-text assert written to fix that searched for
a literal that appears in its own line, so it could never fail. The needle is now built from
two fragments and the wiring is asserted at the guard.

Verified on the merge result: 391 passed, 0 failed, 0/26 skipped, 391 collected (floor 391),
84/84 selftests, 43/43 can-fire, 5/5 gates. Ceilings untouched at 26/7/2; floor stays at
main's 391, since this adds selftest assertions rather than pytest tests. black clean at
line-length 100; ruff at main's baseline of 6 pre-existing findings, none added.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
stranske pushed a commit that referenced this pull request Aug 23, 2026
…asured

Only `.verify-floor.json` conflicted, the collision this file's own note predicts
and which the branch note had already flagged as likely.

#59 (improvement-log accessor) raised the floor 387 -> 391 on main with four new
tests; this branch raised it 387 -> 388 with one. Resolved as the UNION, not by
taking a side: #59's rationale is retained and the count was RE-MEASURED on the
merge result rather than either number being carried over. 392 collected,
independently confirmed by `pytest --collect-only` before trusting the arithmetic
-- taking a side is what once put the floor 8 tests below reality.

verify.py on the merge result: 392 passed, 0 failed, 0 skipped, 84/84 selftests,
43/43 can-fire, 5/5 gates green.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
stranske pushed a commit that referenced this pull request Aug 23, 2026
Only `.verify-floor.json` conflicted. Resolved as the UNION per that file's own rule: #59's
rationale (floor 387 -> 391, the improvement-log accessor's four tests) is retained, this branch's
entry is appended, and the count was RE-MEASURED on the merge result -- 394 -- rather than
arithmetic on either side's number. No ceiling moved; the three new tests need no agent CLI, ledger
or installed runtime, so nothing new is skipped.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
stranske pushed a commit that referenced this pull request Aug 23, 2026
… silent

`.verify-floor.json` has been found BELOW reality four times -- 21 low at the worst, then 8,
then 1, then 2 -- and each was hand-raised after the fact by whoever happened to look. The
stale number was never the defect. `_floor_problems` fired only on `collected < floor`, so a
PR could add tests and never touch the file: silently green, with the floor left permissive
by exactly the number added. #34 and #37 each did precisely that, which is what PR #50 then
had to clean up by hand.

`collected` is now an equality. Too few tests still fails; too many fails as well, printing
the two integers to write and telling you to rebase first.

That also makes the concurrent case self-enforcing, which is the part discipline could not
fix. Once every test-adding branch must edit these same two lines, two concurrent branches
CONFLICT IN GIT -- the second cannot merge without rebasing onto the first and re-measuring
on the actual merge result. This change demonstrated that on itself repeatedly: #42 landed
underneath it (floor 368 -> 387, ceiling 24 -> 26, verify.py reformatted), then #59
(387 -> 391), then #61, all in one afternoon. Each conflict forced a rebase and a fresh
measurement; under the old one-directional rule each would have merged green with a floor
below reality. Git's own conflict detection is what enforces "measure on the merge result,
not on the branch", the rule the note in that file had to restate three times with nothing
behind it.

`passed` deliberately stays a MINIMUM on passed+skipped. Only collection is
machine-invariant, measured across machines at 391 collected on both, with pass/skip splits
of 365/26 on CI against 391/0 locally. Making that one strict too would fail a machine for
honestly naming a missing prerequisite.

Two further fixes in the same change:

* `--update-floor` no longer REPLACES the note. It appends. The note is the only record of
  which prerequisite justifies each ceiling, so overwriting it destroyed the rationale on
  every use -- the file had to carry a warning about its own tool.
* Drift does not block `--update-floor`. The first draft made it a latched gate: a floor
  behind reality became a problem, and the guard was `not problems`, so the one remedy the
  error message named was refused for the existence of the very condition it clears.
  `_blocks_floor_update` lets drift through while real failures still block, sharing
  `DRIFT_PREFIX` so message and predicate cannot diverge.

Selftests cover both directions, skip-invariance, the unset-floor case, the unblock
predicate and note preservation, each with a deliberate-break demonstration. Two of those
tests were themselves defective and only the break demo caught it: the latched-gate assert
exercised `_blocks_floor_update` in isolation and stayed green when the CALL SITE was
reverted (built-but-not-wired), and the source-text assert written to fix that searched for
a literal that appears in its own line, so it could never fail. The needle is now built from
two fragments and the wiring is asserted at the guard.

Verified on the merge result: 391 passed, 0 failed, 0/26 skipped, 391 collected (floor 391),
84/84 selftests, 43/43 can-fire, 5/5 gates. Ceilings untouched at 26/7/2; the floor stays at
main's 391, since this adds selftest assertions rather than pytest tests. black clean at
line-length 100; ruff at main's baseline of 6 pre-existing findings, none added.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
stranske pushed a commit that referenced this pull request Aug 23, 2026
…is redundant

PR #58's entire change to keepalive_evidence.py is two deletions: the dead `haystack` binding
(decided in the previous commit) and this unused `repo = target.split("#", 1)[0]` in the
selftest loop — `record_run` is passed `target`, never `repo`.

Taking both makes this branch's copy of the file a superset of #58's, and since both are now
identical deletions on both sides, the file merges with zero conflicts in either order —
verified with a 3-way merge against origin/claude/ci-conform-format-lint.

`ruff check --select E4,E7,E9,F` is clean for this file and `black --line-length 100 --check`
leaves it unchanged. #58 is separately CONFLICTING against main (it predates #59 and is a
tree-wide black pass), so it needs a rebase regardless — that is not caused by this branch.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
stranske pushed a commit that referenced this pull request Aug 23, 2026
langsmith-fleet-worker-attempt.json is a per-run CI provenance record, not
source. reusable-codex-run.yml (in stranske/Workflows) rewrites it on every
invocation and uploads it as artifact langsmith-fleet-v1-worker-attempt-<pr>.
It became tracked in 2118f57, and because gitignore does not apply to an
already-tracked path, the runner's `git add -A` commit step staged it on every
run: ten commits across PRs #59/#61/#62/#63, one add/add conflict per concurrent
PR, and a silently widened diff on each.

Nothing reads it from the tree. The langsmith-fleet/v1 consumers here
(langsmith_pull.py, langsmith_fetch.py) ingest the NDJSON Actions artifact
`langsmith-fleet.ndjson`; neither names this path. The 30-day artifact upload
already provides the replacement path, so untracking loses no provenance.
stranske/Workflows reached the same verdict for its own copy.

The ignore entry sits outside the synced WORKFLOWS STATUS FILES block, since
that block mirrors a consumer template which does not carry this pattern and
would drop it on the next rewrite.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
stranske added a commit that referenced this pull request Aug 23, 2026
langsmith-fleet-worker-attempt.json is a per-run CI provenance record, not
source. reusable-codex-run.yml (in stranske/Workflows) rewrites it on every
invocation and uploads it as artifact langsmith-fleet-v1-worker-attempt-<pr>.
It became tracked in 2118f57, and because gitignore does not apply to an
already-tracked path, the runner's `git add -A` commit step staged it on every
run: ten commits across PRs #59/#61/#62/#63, one add/add conflict per concurrent
PR, and a silently widened diff on each.

Nothing reads it from the tree. The langsmith-fleet/v1 consumers here
(langsmith_pull.py, langsmith_fetch.py) ingest the NDJSON Actions artifact
`langsmith-fleet.ndjson`; neither names this path. The 30-day artifact upload
already provides the replacement path, so untracking loses no provenance.
stranske/Workflows reached the same verdict for its own copy.

The ignore entry sits outside the synced WORKFLOWS STATUS FILES block, since
that block mirrors a consumer template which does not carry this pattern and
would drop it on the next rewrite.

Co-authored-by: t <t@e>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
stranske pushed a commit that referenced this pull request Aug 23, 2026
…rop a claim #3210 falsifies

This branch ended up with two independent explanations of the same rule, because a
concurrent session added `4b5764a` while this branch already carried its own block.
Both were correct; having both in a file whose header is specifically about
gitignore-comment discipline is not.

Kept from the second block, because the first did not have it:
- the artifact was tracked from 2118f57, not merely "on main until today";
- ten commits across PRs #59/#61/#62/#63 rewrote it, one add/add conflict per
  concurrent PR, silently widening unrelated diffs;
- untracking loses no provenance — the producer already uploads the same bytes as
  Actions artifact `langsmith-fleet-v1-worker-attempt-<pr>` with 30-day retention,
  which is the only reason the file is written at all.

Dropped from it, because it is about to become false: "that block mirrors Workflows'
templates/consumer-repo/.gitignore, which does NOT carry this pattern". Workflows
PR #3210 adds exactly these two patterns to that template at Template-Version 6. The
placement above the managed block is still right, but the reason is that a version
bump rewrites the block wholesale — not that the template lacks the pattern.

Also drops that block's bare literal `langsmith-fleet-worker-attempt.json`, which was
already subsumed by the root-anchored `/langsmith-fleet*.json` above it.

Comments only. Verified identical behaviour with `git check-ignore`: the root artifact
and artifacts/langsmith/ stay ignored, docs/contracts/schemas/langsmith-fleet-v1.schema.json
and langsmith_*.py stay committable, and no tracked file is shadowed by any pattern.
427 collected = floor 427; black, ruff and the 11 guard tests unchanged.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant